﻿using Heng.Elements.Core.Model.Infrastructure.Entities;
using Heng.Elements.Core.Model.Infrastructure.Repositories;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Heng.Elements.Core.Model.Repository.EF.Infrastructure.Contexts
{
    /// <summary>
    /// Entity Framework repository context.
    /// Facade around DbContext.
    /// </summary>
    /// <typeparam name="TContext"> DbContext type. </typeparam>
    public class DbContextFacade<TContext> : IEntityRepositoryContext
        where TContext : DbContext
    {
        #region Member Variables
        private readonly TContext _ctx = null;
        #endregion

        #region Properties
        /// <summary>
        /// Inner DbContext.
        /// </summary>
        public TContext DbContext
        {
            get
            {
                return _ctx;
            }
        }
        #endregion

        /// <summary>
        /// Get entity set.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <returns> Entity set. </returns>
        public DbSet<TEntity> Set<TEntity>()
            where TEntity : class, IEntity
        {
            return _ctx.Set<TEntity>();
        }

        /// <summary>
        /// Ctor.
        /// </summary>
        public DbContextFacade()
        {
            // create DbContext instance
            _ctx = Activator.CreateInstance<TContext>();
            _ctx.Configuration.LazyLoadingEnabled = false;
        }

        /// <summary>
        /// Get entity by ID.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <param name="id"> Entity ID. </param>
        /// <returns> Entity instance. </returns>
        public TEntity GetById<TEntity>(long id)
            where TEntity : class, IEntity
        {
            TEntity entity = this.Set<TEntity>().Where(el => el.Id == id).FirstOrDefault();
            return entity;
        }

        /// <summary>
        /// Get all entities.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <returns> Entities. </returns>
        public IEnumerable<TEntity> GetAll<TEntity>()
            where TEntity : class, IEntity
        {
            IEnumerable<TEntity> entities = this.Set<TEntity>().ToList();
            return entities;
        }

        /// <summary>
        /// Add entity.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <param name="entity"> Entity instance to add. </param>
        public void Add<TEntity>(TEntity entity)
            where TEntity : class, IEntity
        {
            this.DbContext.Set<TEntity>().Add(entity);
        }

        /// <summary>
        /// Save changes for current DbContext.
        /// </summary>
        public void SaveChanges()
        {
            this.DbContext.SaveChanges();
        }

        /// <summary>
        /// Delete entity.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <param name="entity"> Entity instance to be deleted. </param>
        public void Delete<TEntity>(TEntity entity)
            where TEntity : class, IEntity
        {
            this.Set<TEntity>().Remove(entity);
        }

        /// <summary>
        /// Delete entity by ID.
        /// </summary>
        /// <typeparam name="TEntity"> Entity type. </typeparam>
        /// <param name="id"> Entity ID to delete. </param>
        public void Delete<TEntity>(long id)
            where TEntity : class, IEntity
        {
            // get entity by ID
            TEntity entity = this.GetById<TEntity>(id);

            // determine if the entity was found
            if (entity == null)
                throw new Exception(string.Format("No entity of type '{0}' with ID '{1}' was found to be deleted.", typeof(TEntity).FullName, id));

            // delete entity
            this.Delete(entity);
        }

        /// <summary>
        /// Dispose current DbContext.
        /// </summary>
        public void Dispose()
        {
            this.DbContext.Dispose();
        }
    }
}
